useRef
是什麼?useRef
是 React 提供的 hook,用來創建一個 可變(mutable)的引用對象,可以用來存取 DOM 元素或保存跨越多次渲染的資料,且在資料變動時不會觸發 re-render。
useRef
的使用情境useRef
最常見的使用情境之一是存取 DOM 元素,讓我們可以直接操作原生 DOM。
function App() {
const inputRef = useRef(null);
return (
<div>
<label htmlFor='name'>name:</label>
<input ref={inputRef} type='text' />
<div>
<button onClick={() => inputRef.current.focus()}>Click to Focus</button>
</div>
</div>
);
}
在這個例子中,我們使用 useRef
hook 來存取 input
元素,並且在 button
的 onClick
事件中,使用 inputRef.current.focus()
來 focus 到 input 元素。
除了存取 DOM,uuseRef
也可以用來保存那些不會因為 component re-render 而失去的值。
function Counter() {
const [count, setCount] = useState(0);
const renderCount = useRef(0);
renderCount.current += 1;
return (
<div>
<p>Count: {count}</p>
<p>Component rendered {renderCount.current} times</p>
<button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
</div>
);
}
在這個例子中,renderCount 使用 useRef
來保存 render 的次數,這樣就不會因為 count
的改變而重新 render。
count 使用 useState
來保存當前計數器的值 count。,當 count
改變時,會重新 render,但是 renderCount 不會受到影響。
初始渲染時,renderCount.current
會是 1,當 count
改變時,renderCount.current
會持續增加。
useRef
還可以用來追蹤 component 在前一次 render 中的 state 值。
function App() {
const [name, setName] = useState("Alice");
const prevName = useRef("");
useEffect(() => {
prevName.current = name;
}, [name]);
return (
<div>
<p>Current name: {name}</p>
<p>Previous name: {prevName.current}</p>
<button onClick={() => setName("Bob")}>Change Name</button>
</div>
);
}
在這個例子中,我們使用 useRef
來保存前一個 name
的值。每次 name
改變時,prevName.current
都會更新為上一次的 name
。
在某些情況下,我們需要避免副作用多次執行,這時可以使用 useRef 來追蹤是否已經執行過某個操作。
function App() {
const hasFetched = useRef(false);
useEffect(() => {
if (!hasFetched.current) {
console.log("Fetching data...");
hasFetched.current = true;
}
}, []);
return <div>Data has been fetched</div>;
}
這個例子使用 hasFetched
來追蹤是否已經執行過 fetch data 的操作。當 hasFetched.current
為 false
時,會執行 console.log("Fetching data...")
,並將 hasFetched.current
設為 true
,這樣就可以避免重複執行 fetch data 的操作。
// This is a React Quiz from BFE.dev
import * as React from "react";
import { useRef, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
function App() {
const ref = useRef(null);
const [state, setState] = useState(1);
useEffect(() => {
setState(2);
}, []);
console.log(ref.current?.textContent);
return (
<div>
<div ref={state === 1 ? ref : null}>1</div>
<div ref={state === 2 ? ref : null}>2</div>
</div>
);
}
const root = createRoot(document.getElementById("root"));
root.render(<App />);
在初始渲染,react 呼叫 App component function 產生 react element ,並且轉換相對應的 DOM 掛載到螢幕上。
執行 console.log(ref.current?.textContent)
,因為 useRef 的初始值是 null
所以印出 undefined
。
state
為 1,因此第一個 div 元素被引用。
接著,react 執行 useEffect
中 setState(2)
會觸發 re-render。
重新呼叫 App component function 產生 react element 經過 diff 比較後更新 DOM。
執行 console.log(ref.current?.textContent)
,因為 useRef
保存了上一次的值,所以印出"1"。
state
更新為 2,第一個 div 元素的 ref
被設為 null
,第二個 div 元素被引用。